"""
Configuration-Driven Extension

A complete extension implementation that reads its behavior from configuration files
instead of requiring custom Python code.
"""

import fnmatch
from pathlib import Path
from typing import Dict, List, Any, Optional
import logging

from .base_extension import BaseExtension
from .base_builder import BaseBuilder
from .base_runner import BaseRunner
from .base_validator import BaseValidator
from .config_parser import ExtensionDescriptor, config_parser
from .config_driven_builder import ConfigDrivenBuilder
from .config_driven_runner import ConfigDrivenRunner
from .config_driven_validator import ConfigDrivenValidator

logger = logging.getLogger(__name__)


class ConfigDrivenExtension(BaseExtension):
    """
    Extension that defines its behavior through configuration files.
    
    This extension reads YAML/JSON configuration files to determine:
    - Build steps and dependencies
    - Test execution procedures
    - Validation rules
    - Supported frameworks and detection patterns
    - CLI command definitions
    
    This allows creating extensions without writing Python code.
    """
    
    def __init__(self, config_path: Path):
        """
        Initialize the config-driven extension.
        
        Args:
            config_path: Path to the extension configuration file
            
        Raises:
            ValueError: If configuration is invalid
            FileNotFoundError: If configuration file doesn't exist
        """
        # Parse the configuration descriptor
        self.descriptor = config_parser.parse(config_path)
        
        # Validate the descriptor
        errors = config_parser.validate_descriptor(self.descriptor)
        if errors:
            raise ValueError(f"Invalid extension configuration: {'; '.join(errors)}")
        
        # Initialize base class
        super().__init__(self.descriptor.name, self.descriptor.version)
        
        # Store config path for reference
        self.config_path = config_path
        
        # Initialize components lazily
        self._builder = None
        self._runner = None
        self._validator = None
        
        logger.info(f"Initialized config-driven extension: {self.descriptor.name} v{self.descriptor.version}")
    
    @classmethod
    def from_config_file(cls, config_path: Path) -> 'ConfigDrivenExtension':
        """
        Create a config-driven extension from a configuration file.
        
        Args:
            config_path: Path to the extension configuration file
            
        Returns:
            ConfigDrivenExtension instance
        """
        return cls(config_path)
    
    @classmethod
    def can_load_config(cls, config_path: Path) -> bool:
        """
        Check if a configuration file can be loaded as a config-driven extension.
        
        Args:
            config_path: Path to configuration file
            
        Returns:
            True if the file can be loaded
        """
        try:
            return config_parser.can_parse(config_path)
        except Exception:
            return False
    
    @property
    def description(self) -> str:
        """Get a brief description of the extension."""
        return self.descriptor.description
    
    @property
    def supported_frameworks(self) -> List[str]:
        """Get list of supported framework names."""
        return self.descriptor.supported_frameworks
    
    @property
    def config_schema(self) -> Dict[str, Any]:
        """Get JSON schema for extension configuration."""
        return self.descriptor.config_schema
    
    def can_handle_config(self, config_path: str) -> bool:
        """
        Check if this extension can handle the given configuration.
        
        Uses the config_patterns from the descriptor to make the determination.
        
        Args:
            config_path: Path to configuration file
            
        Returns:
            True if extension can handle this configuration
        """
        if not self.descriptor.config_patterns:
            # If no patterns defined, we can't handle configs by default
            return False
        
        config_file = Path(config_path)
        
        # Check if config file exists
        if not config_file.exists():
            return False
        
        # Check filename patterns
        for pattern in self.descriptor.config_patterns:
            if fnmatch.fnmatch(config_file.name, pattern):
                logger.debug(f"Config {config_path} matches pattern: {pattern}")
                return True
        
        # Check content patterns by reading file
        try:
            content = config_file.read_text(encoding='utf-8')
            for pattern in self.descriptor.config_patterns:
                if pattern.startswith('content:'):
                    # Content-based pattern matching
                    search_pattern = pattern[8:]  # Remove 'content:' prefix
                    if search_pattern in content:
                        logger.debug(f"Config {config_path} matches content pattern: {search_pattern}")
                        return True
        except Exception as e:
            logger.debug(f"Failed to read config file {config_path} for pattern matching: {e}")
        
        logger.debug(f"Config {config_path} doesn't match any patterns: {self.descriptor.config_patterns}")
        return False
    
    def get_builder(self) -> BaseBuilder:
        """Get the builder instance for this extension."""
        if self._builder is None:
            self._builder = ConfigDrivenBuilder(self.name, self.descriptor)
        return self._builder
    
    def get_runner(self) -> BaseRunner:
        """Get the runner instance for this extension."""
        if self._runner is None:
            self._runner = ConfigDrivenRunner(self.name, self.descriptor)
        return self._runner
    
    def get_validator(self) -> BaseValidator:
        """Get the validator instance for this extension."""
        if self._validator is None:
            self._validator = ConfigDrivenValidator(self.name, self.descriptor)
        return self._validator
    
    def get_config_descriptor(self) -> Optional[Dict[str, Any]]:
        """
        Get configuration descriptor for config-driven extensions.
        
        Returns:
            Configuration descriptor dictionary
        """
        return {
            'type': 'config_driven',
            'config_path': str(self.config_path),
            'descriptor': {
                'name': self.descriptor.name,
                'version': self.descriptor.version,
                'description': self.descriptor.description,
                'supported_frameworks': self.descriptor.supported_frameworks,
                'dependencies': self.descriptor.dependencies,
                'build_steps_count': len(self.descriptor.build_steps),
                'test_steps_count': len(self.descriptor.test_steps),
                'validation_rules_count': len(self.descriptor.validation_rules),
                'config_patterns': self.descriptor.config_patterns,
                'source_patterns': self.descriptor.source_patterns,
            }
        }
    
    def get_dependencies(self) -> List[str]:
        """
        Get list of other extensions this extension depends on.
        
        Returns:
            List of extension names from the descriptor
        """
        return self.descriptor.dependencies
    
    def get_commands(self) -> Dict[str, Any]:
        """
        Get custom CLI commands provided by this extension.
        
        Returns:
            Dictionary mapping command names to command definitions from descriptor
        """
        return self.descriptor.commands
    
    def validate_environment(self) -> List[str]:
        """
        Validate that the environment meets extension requirements.
        
        Returns:
            List of error messages (empty if valid)
        """
        errors = []
        
        # Use the config-driven validator to check environment
        try:
            validator = self.get_validator()
            result = validator.validate_environment()
            
            if not result.success:
                errors.append(f"Environment validation failed: {result.message}")
        except Exception as e:
            errors.append(f"Failed to validate environment: {str(e)}")
        
        return errors
    
    def initialize(self) -> None:
        """
        Initialize the extension (called after loading).
        
        Performs any setup required by the configuration.
        """
        super().initialize()
        
        logger.debug(f"Initializing config-driven extension: {self.name}")
        
        # Validate that all required components can be created
        try:
            builder = self.get_builder()
            runner = self.get_runner()
            validator = self.get_validator()
            
            # Perform basic validation
            dependency_errors = builder.validate_dependencies()
            if dependency_errors:
                logger.warning(f"Builder dependency issues for {self.name}: {dependency_errors}")
            
        except Exception as e:
            logger.error(f"Failed to initialize components for {self.name}: {e}")
            raise
        
        logger.info(f"Config-driven extension initialized: {self.name}")
    
    def cleanup(self) -> None:
        """
        Clean up extension resources (called before unloading).
        """
        super().cleanup()
        
        # Clear component references
        self._builder = None
        self._runner = None
        self._validator = None
        
        logger.debug(f"Config-driven extension cleaned up: {self.name}")
    
    def get_info(self) -> Dict[str, Any]:
        """
        Get detailed information about this extension.
        
        Returns:
            Dictionary with extension information
        """
        return {
            'name': self.name,
            'version': self.version,
            'description': self.description,
            'type': 'config_driven',
            'enabled': self.enabled,
            'config_path': str(self.config_path),
            'supported_frameworks': self.supported_frameworks,
            'dependencies': self.get_dependencies(),
            'has_builder': len(self.descriptor.build_steps) > 0,
            'has_runner': len(self.descriptor.test_steps) > 0,
            'has_validator': len(self.descriptor.validation_rules) > 0,
            'build_steps': len(self.descriptor.build_steps),
            'test_steps': len(self.descriptor.test_steps),
            'validation_rules': len(self.descriptor.validation_rules),
            'config_patterns': self.descriptor.config_patterns,
            'source_patterns': self.descriptor.source_patterns,
            'commands': list(self.descriptor.commands.keys()) if self.descriptor.commands else [],
        }
    
    def reload_config(self) -> bool:
        """
        Reload the extension configuration from file.
        
        Returns:
            True if reload succeeded
        """
        try:
            logger.info(f"Reloading config for extension: {self.name}")
            
            # Parse new configuration
            new_descriptor = config_parser.parse(self.config_path)
            
            # Validate new configuration
            errors = config_parser.validate_descriptor(new_descriptor)
            if errors:
                logger.error(f"Invalid reloaded configuration: {'; '.join(errors)}")
                return False
            
            # Update descriptor and clear cached components
            self.descriptor = new_descriptor
            self._builder = None
            self._runner = None
            self._validator = None
            
            logger.info(f"Successfully reloaded config for extension: {self.name}")
            return True
            
        except Exception as e:
            logger.error(f"Failed to reload config for extension {self.name}: {e}")
            return False
    
    def __str__(self) -> str:
        """String representation of the extension."""
        return f"{self.name} v{self.version} (config-driven)"
    
    def __repr__(self) -> str:
        """Detailed string representation."""
        return (f"<ConfigDrivenExtension(name='{self.name}', version='{self.version}', "
                f"enabled={self.enabled}, config_path='{self.config_path}')>")